package com.ruoyi.business.processIou;

import com.ruoyi.bizsys.domain.*;
import com.ruoyi.bizsys.enums.EnumFeeType;
import com.ruoyi.bizsys.mapper.AccountFlowMapper;
import com.ruoyi.bizsys.mapper.LoanIouDeferredMapper;
import com.ruoyi.bizsys.mapper.LoanIouMapper;
import com.ruoyi.bizsys.mapper.LoanOffsetMapper;
import com.ruoyi.bizsys.service.ILoanOffsetService;
import com.ruoyi.bizsys.service.IRepaymentOrderService;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.bizsys.vo.LoanIouExtVo;
import com.ruoyi.business.processIou.service.ILoanIouChangeRecordService;
import com.ruoyi.business.processIou.service.ILoanOffsetChangeRecordService;
import com.ruoyi.business.service.IBLoanInfoService;
import com.ruoyi.business.service.asyncservice.AsyncRepaymentService;
import com.ruoyi.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * @author wqg
 * @description 单个借据号进行处理，更新iou和校验offset
 * @date 2022/3/23
 */
@Slf4j
@Component
public class ProcessForIouServiceForExceptionData {
    @Resource
    LoanIouMapper loanIouMapper;
    @Resource
    LoanOffsetMapper loanOffsetMapper;
    @Autowired
    ILoanIouChangeRecordService loanIouChangeRecordService;
    @Autowired
    ILoanOffsetChangeRecordService loanOffsetChangeRecordService;

    @Autowired
    AsyncRepaymentService asyncRepayMentService;

    @Autowired
    AccountFlowMapper accountFlowMapper;

    @Autowired
    LoanIouDeferredMapper loanIouDeferredMapper;

    @Autowired
    ILoanOffsetService loanOffsetService;

    @Autowired
    IBLoanInfoService iBLoanInfoService;

    @Autowired
    IRepaymentOrderService repaymentOrderService;

    /***显示当天的还款金额*/
    private static ThreadLocal<BigDecimal> amtOrgLocal = new ThreadLocal<>();
    /***累计罚息-用于扣除*/
    private static ThreadLocal<BigDecimal> lateFeePlusLocal = new ThreadLocal<>();
    /***累计罚息-一直累计*/
    private static ThreadLocal<BigDecimal> lateFeePlusAllLocal = new ThreadLocal<>();
    /***按天存储的offset*/
    private static ThreadLocal<TreeMap<Integer, List<LoanOffset>>> dayMapLocal = new ThreadLocal<>();
    /***原始数据*/
    private static ThreadLocal<LoanIou> loanIouOrgLocal = new ThreadLocal<>();
    /***当天iou*/
    private static ThreadLocal<LoanIou> loanIouDay = new ThreadLocal<>();

    /**
     * 是否延期中
     **/
    private static ThreadLocal<Boolean> isDefered = new ThreadLocal<>();

    /**到那天结束延期**/
    private static ThreadLocal<Integer> deferredDay = new ThreadLocal<>();

    /** 当前延期的编号 **/
    private static ThreadLocal<Long> loanSerialDeferredNo = new ThreadLocal<>();
    /** 本次延期的剩余利息 **/
    private static ThreadLocal<BigDecimal> deferredInterestFee = new ThreadLocal<>();
    /** 本次延期的剩余服务费 **/
    private static ThreadLocal<BigDecimal> deferredServiceFee = new ThreadLocal<>();

//    @PostConstruct
//    public void test() {
//        initForOne("20220814082754132853166081");
//    }
    /**
     * 全量计算回溯，返回iou数据
     *
     * @param loanSerialNo
     * @return
     */
    public LoanIou init(String loanSerialNo) {
        setLocalData();
        log.info(Thread.currentThread().getName() + "开始处理：{}", loanSerialNo);
        LoanIouExtVo loanIou = loanIouMapper.findOneByLoanSerialNo(loanSerialNo);
        // 保留原始数据，用于对比数据
        BeanUtils.copyProperties(loanIou, loanIouOrgLocal.get());
        log.info("产品信息：periodNum：{},loanAmt:{},interest：{},serviceFee：{},dateLateFee:{},lateFeeMaxDays:{}"
                , loanIou.getPeriodNum(), loanIou.getLoanAmt()
                , loanIou.getInterest(), loanIou.getServiceFee()
                , loanIou.getDateLateFee(), loanIou.getLateFeeMaxDays());
        doProcessOffset(loanIou);
        return loanIou;
    }

    public LoanIou initForOne(String loanSerialNo) {
        setLocalData();
        log.info(Thread.currentThread().getName() + "开始处理：{}", loanSerialNo);
        LoanIouExtVo loanIou = loanIouMapper.findOneByLoanSerialNoForOne(loanSerialNo);
        // 保留原始数据，用于对比数据
        BeanUtils.copyProperties(loanIou, loanIouOrgLocal.get());
        log.info("产品信息：periodNum：{},loanAmt:{},interest：{},serviceFee：{},dateLateFee:{},lateFeeMaxDays:{}"
                , loanIou.getPeriodNum(), loanIou.getLoanAmt()
                , loanIou.getInterest(), loanIou.getServiceFee()
                , loanIou.getDateLateFee(), loanIou.getLateFeeMaxDays());
        doProcessOffset(loanIou);
        return loanIou;
    }


    /**
     * 初始化local变量
     */
    private void setLocalData() {
        loanIouOrgLocal.set(new LoanIou());
        loanIouDay.set(new LoanIou());
        amtOrgLocal.set(BigDecimal.ZERO.setScale(6, BigDecimal.ROUND_DOWN));
        lateFeePlusLocal.set(BigDecimal.ZERO.setScale(6, BigDecimal.ROUND_DOWN));
        lateFeePlusAllLocal.set(BigDecimal.ZERO.setScale(6, BigDecimal.ROUND_DOWN));
        dayMapLocal.set(new TreeMap<>());
        isDefered.set(false);
        deferredDay.set(0);
        loanSerialDeferredNo.set(1L);// 不能设置0L 表设计为不为空
        deferredInterestFee.set(BigDecimal.ZERO);
        deferredServiceFee.set(BigDecimal.ZERO);
    }

    /**
     * 手动删除，防止内存泄漏
     */
    private void removeLocalData() {
        loanIouOrgLocal.remove();
        loanIouDay.remove();
        amtOrgLocal.remove();
        lateFeePlusLocal.remove();
        lateFeePlusAllLocal.remove();
        dayMapLocal.remove();
        isDefered.remove();
        deferredDay.remove();
        loanSerialDeferredNo.remove();
        deferredInterestFee.remove();
        deferredServiceFee.remove();
    }

    /**
     * 开始计算
     *
     * @param loanIou
     */
    private void doProcessOffset(LoanIouExtVo loanIou) {
        // dayMap key 天 value 还款amt
        List<LoanOffset> loanOffsetList = loanOffsetMapper.selectListForIou(loanIou.getLoanSerialNo());
        if (!CollectionUtils.isEmpty(loanOffsetList)) {
            dayMapLocal.set(loanOffsetListToTreeMap(loanOffsetList));
        }
        // 借据表的period_num周期内，利息是由本金*周期天数*利息率，在0天生成，服务费由本金*服务费率，在0天生成。
        // 如果发生还款优先偿还利息，然后才是服务费，一旦偿还到0后面一直保持为0
        loanIou.setLateFee(BigDecimal.ZERO);
        // 方便打印数据 金融周期内还款不会产生罚息
        int periodNum = loanIou.getPeriodNum() == null ? 0 : loanIou.getPeriodNum();
        int rightDays = periodNum + loanIou.getLateFeeGracePeriod();
        // 判断是不是延期中，
        LoanIouDeferred loanIouDeferred = new LoanIouDeferred();
        if (null != loanIou.getIsDeferred() && loanIou.getIsDeferred() == 1) {
            loanIouDeferred = loanIouDeferredMapper.getDetailByDeferNo(loanIou.getLoanSerialDeferredNo());
        }
        for (int i = 0; i <= rightDays; i++) {
            if (i <= loanIou.getNowDayIndex()) {
                doProcessInPeriodNumByDay(i, dayMapLocal.get().get(i), loanIou, loanIouDeferred);
            }
        }
        if (loanIou.getLoanAmt().compareTo(BigDecimal.ZERO) == 0) {
            log.info(loanIou.getLoanSerialNo() + ":周期内还款完成～没有逾期");
            //return;
        }
        // 购买延期可能有罚息需要计算罚息
        log.info(loanIou.getLoanSerialNo() + "正常,periodNum天数：{},宽限日:{}", periodNum, loanIou.getLateFeeGracePeriod());
        if (null != loanIou.getIsDeferred() && loanIou.getIsDeferred() == 1) {
            // 存在延期 获取延期日 + 宽限日
            periodNum = loanIou.getDeferredPeriodNum();
            log.info(loanIou.getLoanSerialNo() + "存在延期,periodNum天数：{},宽限日:{}", periodNum, loanIou.getLateFeeGracePeriod());
            for (int i = rightDays + 1; i <= periodNum; i++) {
                if (i <= loanIou.getNowDayIndex()) {
                    doProcessInLateFree(i, dayMapLocal.get().get(i), loanIou, loanIouDeferred);
                }
            }
            // 延期计算完后后面需要用到
            rightDays = periodNum;
        }
        if (loanIou.getLoanAmt().compareTo(BigDecimal.ZERO) == 0) {
            log.info(loanIou.getLoanSerialNo() + ":延期周期内还款完成～没有逾期");
            // return;
        }

        // 存在还款 进行计算 进行罚息 < 优先偿还利息，然后才是服务费，一旦偿还到0后面一直保持为0 >
        log.info(loanIou.getLoanSerialNo() + "-滞纳金按日收费宽限期 {} 天", loanIou.getLateFeeGracePeriod());
        isDefered.set(false);// 延期到期了
        for (int i = rightDays + 1; i <= rightDays + loanIou.getLateFeeMaxDays(); i++) {
            // 先进行第一个部分计算
            if (i <= loanIou.getNowDayIndex()) {
                doProcessInLateFree(i, dayMapLocal.get().get(i), loanIou, loanIouDeferred);
            }
        }
        log.info("大于LateFeeMaxDays的计算，大于 产品限制滞纳金天数后，取滞纳金限制天数最后一天");

        doProcessOverMaxDay(dayMapLocal.get(), loanIou, loanIouDeferred);

        // 判断iou是否数据正确进行更新
        // if(loanIouOrgLocal.get().getLateFee().compareTo(lateFeePlusAllLocal.get())!=0){
        if (compareIouRepaid(loanIouOrgLocal.get(), loanIou, lateFeePlusLocal.get(), lateFeePlusAllLocal.get())) {
            log.info(loanIou.getLoanSerialNo() + " 》》》》》》iou还款利息错误");
            // 进行更新iou操作 和记录变更记录
            loanIouChangeRecordService.updateAndCreateRecord(loanIouOrgLocal.get(), lateFeePlusLocal.get(), lateFeePlusAllLocal.get(), loanIou);
        }

        removeLocalData();
        log.info("-----------:结束");
    }

    /**
     * @param orgLoanIou     数据库源数据
     * @param loanIouNow     temp数据
     * @param lateFeePlus    剩余滞纳金
     * @param lateFeePlusAll 总滞纳金
     * @return
     */
    private boolean compareIouRepaid(LoanIou orgLoanIou, LoanIou loanIouNow, BigDecimal lateFeePlus, BigDecimal lateFeePlusAll) {
        if (orgLoanIou.getLateFee().compareTo(lateFeePlusAll.setScale(4, BigDecimal.ROUND_DOWN)) != 0) {
            return true;
        }
        if (orgLoanIou.getRepaidLateFee().compareTo(lateFeePlusAll.subtract(lateFeePlus).setScale(4, BigDecimal.ROUND_DOWN)) != 0) {
            return true;
        }
        if (orgLoanIou.getRepaidInterest().compareTo(orgLoanIou.getLoanInterest().subtract(loanIouNow.getInterest()).setScale(4, BigDecimal.ROUND_DOWN)) != 0) {
            return true;
        }
        if (orgLoanIou.getRepaidServiceFee().compareTo(orgLoanIou.getServiceFee().subtract(loanIouNow.getServiceFee()).setScale(4, BigDecimal.ROUND_DOWN)) != 0) {
            return true;
        }
        if (orgLoanIou.getRepaidPrincipal().compareTo(orgLoanIou.getLoanPrincipal().subtract(loanIouNow.getLoanAmt()).setScale(4, BigDecimal.ROUND_DOWN)) != 0) {
            return true;
        }
        return false;
    }

    /**
     * 还款期限内按天进行计算
     *
     * @param i       是第几天
     * @param dayList
     * @param loanIou
     */
    private void doProcessInPeriodNumByDay(int i, List<LoanOffset> dayList, LoanIouExtVo loanIou, LoanIouDeferred loanIouDeferred) {
        // 本期还的钱
        BigDecimal amt = CollectionUtils.isEmpty(dayList) ? BigDecimal.ZERO : dayList.get(0).getAmt().setScale(6, BigDecimal.ROUND_DOWN);
        if (amt.compareTo(BigDecimal.ZERO) != 0) {
            for (int j = 0; j < dayList.size(); j++) {
                amt = dayList.get(j).getAmt().setScale(6, BigDecimal.ROUND_DOWN);
                // 利息计算
                amtOrgLocal.set(amt);
                processInterest(i, j, amt, loanIou, dayList.get(j), loanIouDeferred);
            }
        } else {
            amtOrgLocal.set(BigDecimal.ZERO);
            endProcess(i, 0, amt, loanIou);
        }
    }

    /**
     * 超期后进行罚息计算等
     *
     * @param i
     * @param dayList
     * @param loanIou
     */
    private void doProcessInLateFree(int i, List<LoanOffset> dayList, LoanIouExtVo loanIou, LoanIouDeferred loanIouDeferred) {
        if (isDefered.get() && i < deferredDay.get()) {
            lateFeePlusLocal.set(BigDecimal.ZERO);
            // 今日罚息
            loanIou.setLateFee(BigDecimal.ZERO);
            // 延期周期内的其他还款
            doProcessInPeriodNumByDay(i, dayMapLocal.get().get(i), loanIou, loanIouDeferred);
            return;
        }
        // 计算罚息 罚息的计算只依照前一天的本金(前一天的本金 * 罚息利率dateLateFee )
        BigDecimal lateFee = loanIou.getLoanAmt().multiply(loanIou.getDateLateFee()).setScale(6, BigDecimal.ROUND_DOWN);
        // 今日罚息
        loanIou.setLateFee(lateFee);
        // 加上今天的罚息
        lateFeePlusLocal.set(lateFeePlusLocal.get().add(lateFee).setScale(6, BigDecimal.ROUND_DOWN));
        lateFeePlusAllLocal.set(lateFeePlusAllLocal.get().add(lateFee));
        BigDecimal amt = CollectionUtils.isEmpty(dayList) ? BigDecimal.ZERO : dayList.get(0).getAmt().setScale(6, BigDecimal.ROUND_DOWN);
        if (amt.compareTo(BigDecimal.ZERO) != 0) {
            for (int j = 0; j < dayList.size(); j++) {
                amt = dayList.get(j).getAmt().setScale(6, BigDecimal.ROUND_DOWN);
                amtOrgLocal.set(amt);
                if (isDeferOffset(dayList.get(j).getActionType())  && i < deferredDay.get()) {
                    // 当天还款不为空，且是延期还款
                    lateFeePlusLocal.set(BigDecimal.ZERO);
                    // 今日罚息
                    loanIou.setLateFee(BigDecimal.ZERO);
                    lateFeePlusAllLocal.set(BigDecimal.ZERO);
                    processLateFree(i, j, amt, loanIou, dayList.get(j), loanIouDeferred);
                    // 标示当前是延期
                    isDefered.set(true);
                } else {
                    if (isDefered.get()  && i < deferredDay.get()) {
                        // 延期周期不产生罚息
                        lateFeePlusLocal.set(BigDecimal.ZERO);
                        // 延期周期内的其他还款
                        doProcessInPeriodNumByDay(i, dayMapLocal.get().get(i), loanIou, loanIouDeferred);
                    } else {
                        processLateFree(i, j, amt, loanIou, dayList.get(j), loanIouDeferred);
                    }

                }

            }
        } else {
            amtOrgLocal.set(BigDecimal.ZERO);
            endProcess(i, 0, amt, loanIou);
        }
    }

    /**
     * ·
     * 超期还款，滞纳金取最后一个
     *
     * @param dayMap
     * @param loanIou
     */
    private void doProcessOverMaxDay(TreeMap<Integer, List<LoanOffset>> dayMap, LoanIouExtVo loanIou, LoanIouDeferred loanIouDeferred) {
        int periodNum = loanIou.getPeriodNum() + loanIou.getLateFeeGracePeriod();
        if (null != loanIou.getIsDeferred() && loanIou.getIsDeferred() == 1) {
            periodNum = loanIou.getDeferredPeriodNum();
        }
        for (int day : dayMap.keySet()) {
            // 取大于最大限制日的还款进行计算
            if (day > (periodNum + loanIou.getLateFeeMaxDays())) {
                doProcessOverMaxDayDetial(day, loanIou, dayMap.get(day), loanIouDeferred);
            }

        }
    }

    private void doProcessOverMaxDayDetial(int i, LoanIouExtVo loanIou, List<LoanOffset> dayMap, LoanIouDeferred loanIouDeferred) {
        for (int j = 0; j < dayMap.size(); j++) {
            // 今日罚息不再累计算，lateFeePlus总罚息就是当前总罚息
            loanIou.setLateFee(BigDecimal.ZERO);
            amtOrgLocal.set(dayMap.get(j).getAmt());
            processLateFree(i, j, amtOrgLocal.get(), loanIou, dayMap.get(j), loanIouDeferred);
        }
    }


    /**
     * 罚息扣除计算
     *
     * @param i
     * @param amt
     * @param loanIou
     */
    private void processLateFree(int i, int j, BigDecimal amt, LoanIouExtVo loanIou, LoanOffset loanOffset, LoanIouDeferred loanIouDeferred) {
        // 小于等于罚息
        if (lateFeePlusLocal.get().compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(lateFeePlusLocal.get()) <= 0) {
            lateFeePlusLocal.set(lateFeePlusLocal.get().subtract(amt).setScale(6, BigDecimal.ROUND_DOWN));// 剩余罚息
            loanIou.setOffsetLateFee(amt);
            endProcess(i, j, amt, loanIou);
            return;
        }
        if (lateFeePlusLocal.get().compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(lateFeePlusLocal.get()) == 1) {
            amt = amt.subtract(lateFeePlusLocal.get()).setScale(6, BigDecimal.ROUND_DOWN);// 扣除罚息
            loanIou.setOffsetLateFee(lateFeePlusLocal.get());
            lateFeePlusLocal.set(BigDecimal.ZERO);// 罚息还完
            // 进行利息等计算
        }
        processInterest(i, j, amt, loanIou, loanOffset, loanIouDeferred);
        //  计算是否扣除罚息------end
    }


    /**
     * 计算利息
     *
     * @param i
     * @param j       当天第几次还款0+1
     * @param amt
     * @param loanIou
     */
    private void processInterest(int i, int j, BigDecimal amt, LoanIouExtVo loanIou, LoanOffset loanOffset, LoanIouDeferred loanIouDeferred) {
        BigDecimal interest = loanIou.getInterest();
        if (interest.compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(interest) <= 0) {
            // 小于等于利息
            // 剩余利息
            loanIou.setInterest(interest.subtract(amt).setScale(6, BigDecimal.ROUND_DOWN));
            loanIou.setOffsetInterest(amt);
            endProcess(i, j, amt, loanIou);
            return;
        }
        if (interest.compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(interest) == 1) {
            // 大于利息
            // 进行利息扣除
            amt = amt.subtract(interest).setScale(6, BigDecimal.ROUND_DOWN);
            // 利息还完了
            loanIou.setInterest(BigDecimal.ZERO);
            loanIou.setOffsetInterest(interest);
        }
        // 支付了利息~进行服务费计算
        processServiceFee(i, j, amt, loanIou, loanOffset, loanIouDeferred);
    }

    /**
     * 支付了利息~进行服务费计算
     *
     * @param i
     * @param loanIou
     */
    private void processServiceFee(int i, int j, BigDecimal amt, LoanIouExtVo loanIou, LoanOffset loanOffset, LoanIouDeferred loanIouDeferred) {
        // 支付了利息~进行服务费计算
        BigDecimal serviceFee = loanIou.getServiceFee();
        if (serviceFee.compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(serviceFee) <= 0) {
            // 小于服务费
            serviceFee = serviceFee.subtract(amt).setScale(6, BigDecimal.ROUND_DOWN);
            // 剩余服务费
            loanIou.setServiceFee(serviceFee);
            loanIou.setOffsetServiceFee(amt);
            endProcess(i, j, amt, loanIou);
            return;
        }
        if (serviceFee.compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(serviceFee) == 1) {
            // 大于服务费
            // 扣除服务费
            amt = amt.subtract(serviceFee).setScale(6, BigDecimal.ROUND_DOWN);
            // 服务费还完了
            loanIou.setServiceFee(BigDecimal.ZERO);
            loanIou.setOffsetServiceFee(serviceFee);
        }
        if (isDeferOffset(loanOffset.getActionType())) {
            // 查询子借据数据，并设置当前子借据还款延期费用的情况
            // 注意，组合支付需要计算子借据的剩余已还的数据
            loanIouDeferred = loanIouDeferredMapper.getDetailByDeferNo(loanOffset.getLoanSerialDeferredNo());
            if(!loanSerialDeferredNo.get().equals(loanIouDeferred.getLoanSerialDeferredNo())){
                // 不相等表示这次延期回溯过了，相等表示延期组合支付还没走完
                loanSerialDeferredNo.set(loanIouDeferred.getLoanSerialDeferredNo());
                deferredInterestFee.set(loanIouDeferred.getInterestFee());
                deferredServiceFee.set(loanIouDeferred.getServiceFee());
                // 设置当前延期的天数，截止天数，用于回溯计算到这天时是否需要先计算罚息
                deferredDay.set(deferredDay.get()+loanIou.getPeriodNum()+loanIouDeferred.getPeriodNum()+1);// 还款日当天不算罚息
                log.info(loanIou.getLoanSerialNo() + " [ " + DateUtils.dateTime(DateUtils.addDays(loanIou.getCreateTime(), i)) + " ]罚息计算在第{}天购买延期包,剩余延期利息:{},剩余延期服务费:{}",
                        i,deferredInterestFee.get(),deferredServiceFee.get());
            }
            isDefered.set(true);
            processDeferInterestFee(i, j, amt, loanIou, loanOffset);
        } else {
            // 开始还本金
            processLoanAmt(i, j, amt, loanIou);
        }
    }

    private boolean isDeferOffset(String actionType) {
        String[] actionTypes = {
                BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATEDEFER
                , BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATEDEFER_CARD_STACk
                , BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATEDEFER_WALLET
                , BizConstants.LOAN_REPAYMENT_ORDER_TYPE_COLLECTION_WALLET_BUY_DEFER
        };
        return Arrays.stream(actionTypes).anyMatch((iteam) -> iteam.equals(actionType));
    }

    /**
     * 计算延期的利息
     *
     * @param i
     * @param j
     * @param amt
     * @param loanIou
     */
    private void processDeferInterestFee(int i, int j, BigDecimal amt, LoanIouExtVo loanIou, LoanOffset loanOffset) {

        //如果金额大于0
        if (amt.compareTo(BigDecimal.ZERO) > 0) {
            //还需要还的
            BigDecimal need = deferredInterestFee.get();
            if (need.compareTo(BigDecimal.ZERO) > 0) {
                //还需要还大于0
                BigDecimal result = amt.subtract(need);//冲账后
                if (result.compareTo(BigDecimal.ZERO) > 0) {
                    //冲账后大于 0
                    loanOffset.setOffsetDeferredInterestFee(need);
                    deferredInterestFee.set(BigDecimal.ZERO);// 标记已经全部换了
                    amt = result;
                    // 继续还延期服务费
                    processDeferServiceFee(i, j, amt, loanIou, loanOffset);
                } else {
                    loanOffset.setOffsetDeferredInterestFee(amt);
                    // 计算剩余延期利息
                    deferredInterestFee.set(deferredInterestFee.get().subtract(amt));
                    amt = BigDecimal.ZERO;
                    endProcess(i, j, amt, loanIou);
                    return;
                }
            } else {
                // 继续还延期服务费
                processDeferServiceFee(i, j, amt, loanIou, loanOffset);
            }
        }
    }

    /**
     * 冲账延期服务费
     *
     * @param i
     * @param j
     * @param amt
     * @param loanIou
     * @param loanOffset
     */
    private void processDeferServiceFee(int i, int j, BigDecimal amt, LoanIouExtVo loanIou, LoanOffset loanOffset) {
        // 冲账延期的服务费
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal serviceFeeNeed = deferredServiceFee.get();//还需要还的
            if (serviceFeeNeed.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal serviceFeeResult = amt.subtract(serviceFeeNeed);//冲账滞纳金后
                if (serviceFeeResult.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetDeferredServiceFee(serviceFeeNeed);
                    deferredServiceFee.set(BigDecimal.ZERO);// 延期的服务费还完
                    amt = serviceFeeResult;
                    // 开始还本金
                    processLoanAmt(i, j, amt, loanIou);
                } else {
                    loanOffset.setOffsetDeferredServiceFee(amt);
                    deferredServiceFee.set(deferredServiceFee.get().subtract(amt));// 计算剩余的延期的服务费
                    amt = BigDecimal.ZERO;
                    endProcess(i, j, amt, loanIou);
                    return;
                }
            }
        }
    }


    /**
     * 开始还本金
     *
     * @param i
     * @param loanIou
     */
    private void processLoanAmt(int i, int j, BigDecimal amt, LoanIouExtVo loanIou) {
        LoanOffset loanOffset = dayMapLocal.get().get(i).get(j);
        amt = amt.subtract(loanOffset.getSurplusAmt());// 扣除进入溢缴款的钱进行计算
        BigDecimal loanAmt = loanIou.getLoanAmt();
        // 小于本金
        if (loanAmt.compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(loanAmt) <= 0) {
            // 剩余本金
            loanAmt = loanAmt.subtract(amt).setScale(6, BigDecimal.ROUND_DOWN);
            loanIou.setLoanAmt(loanAmt);
            loanIou.setOffsetPrincipal(amt);
            endProcess(i, j, amt, loanIou);
            return;
        }
        // 大于本金
        if (loanAmt.compareTo(BigDecimal.ZERO) != 0 && amt.compareTo(loanAmt) == 1) {
            // 还完了
            amt = amt.subtract(loanAmt).setScale(6, BigDecimal.ROUND_DOWN);
            loanIou.setLoanAmt(BigDecimal.ZERO);
            loanIou.setOffsetPrincipal(loanAmt);
            endProcess(i, j, amt, loanIou);
            log.info(loanIou.getLoanSerialNo() + ":存在多还款多{}", amt);
        } else {
            log.info(loanIou.getLoanSerialNo() + ":存在多还款多{}", amt);
        }
        doOverFlowAndWallet(i,j,amt);
    }

    // 处理多余的钱进入溢缴款，需要判断该条数据之前是否已经进入了溢缴款，需要减掉
    private void doOverFlowAndWallet(int i, int j, BigDecimal amt) {
        log.info("存在多还款多，进入溢缴款{},{},{}",i,j,amt);
        LoanOffset loanOffset = dayMapLocal.get().get(i).get(j);
        log.info("存在多还款多，进入溢缴款计算后：amt{}",amt);
        if(amt.compareTo(BigDecimal.ZERO)>0){
            // 这里判断金额要加回已经进入溢缴款的钱
            loanOffset.setSurplusAmt(amt.add(loanOffset.getSurplusAmt()));
            if(loanOffset.getSurplusAmt().compareTo(loanOffset.getAmt()) == 0){
                loanOffset.setLateFee(BigDecimal.ZERO);
                loanOffset.setState(BizConstants.BIZ_STATUS_0);
                loanOffset.setRemark(loanOffset.getRemark()+":-1回溯进行溢缴款操作");
            }
            loanOffsetMapper.updateById(loanOffset);
            RepaymentOrder repaymentOrder  = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
            iBLoanInfoService.doOverFlowAndWallet(amt,repaymentOrder);
        }else{
            log.info("未进行更新，计算后：amt{},存在已有的溢缴款",amt);
        }
    }

    /**
     * 计算每一天的最后结果进行处理
     *
     * @param i
     * @param amt
     * @param loanIou
     */
    private void endProcess(int i, int j, BigDecimal amt, LoanIouExtVo loanIou) {
        log.info(
                loanIou.getLoanSerialNo() + " [ " + DateUtils.dateTime(DateUtils.addDays(loanIou.getCreateTime(), i)) + " ]罚息计算在第{}天第{}次:还钱：{},剩余本金：{},剩余利息：{},剩余服务费：{}，当天产生罚息:{},还款后剩余总罚息：{}，产生罚息：{}"
                , i, j, amtOrgLocal.get(), loanIou.getLoanAmt(), loanIou.getInterest(), loanIou.getServiceFee()
                , loanIou.getLateFee() == null ? BigDecimal.ZERO : loanIou.getLateFee()
                , lateFeePlusLocal.get(), lateFeePlusAllLocal.get());
        //记录每天的罚息到账目流水数据
        // createAccountFlow(loanIou, i, j);

        // 存在还款，判断还款数据是否正确
        if (dayMapLocal.get().get(i) != null
                && compareRepaidAmt(dayMapLocal.get().get(i).get(j), loanIouDay.get(), lateFeePlusAllLocal.get())) {
            log.info(
                    loanIou.getLoanSerialNo() + " -----------在第{}天还款计算错误"
                    , i, dayMapLocal.get().get(i).get(j).getLateFee(), lateFeePlusAllLocal.get());
            loanOffsetChangeRecordService.updateAndCreateRecord(dayMapLocal.get().get(i).get(j), lateFeePlusLocal.get(), lateFeePlusAllLocal.get(), loanIou, loanIouDay.get());
        }
        // 记录当天的数据iou当前快照
        loanIouDay.get().setRepaidLateFee(lateFeePlusAllLocal.get().subtract(lateFeePlusLocal.get()));
        loanIouDay.get().setRepaidInterest(loanIouOrgLocal.get().getInterest().setScale(6, BigDecimal.ROUND_DOWN).subtract(loanIou.getInterest().setScale(6, BigDecimal.ROUND_DOWN)));
        loanIouDay.get().setRepaidServiceFee(loanIouOrgLocal.get().getServiceFee().subtract(loanIou.getServiceFee()));
        loanIouDay.get().setRepaidPrincipal(loanIouOrgLocal.get().getLoanPrincipal().subtract(loanIou.getLoanAmt()));
        setZeroOffset(loanIou);
    }

    /**
     * 记录还款罚息
     *
     * @param loanIou
     */
    private void createAccountFlow(LoanIouExtVo loanIou, int i, int j) {
        // 判断每天的第0次就行
        if (j > 0) {
            return;
        }
        BigDecimal lateFee = loanIou.getLateFee() == null ? BigDecimal.ZERO : loanIou.getLateFee();
        if (lateFee.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        AccountFlow accountFlow = new AccountFlow();
        accountFlow.setCustNo(loanIou.getCustNo());
        accountFlow.setRelativeSerialNo(loanIou.getLoanSerialNo());
        accountFlow.setTransferChannel("");
        // 发生的时间 天数+iou创建的时间
        Date transDay = DateUtils.addDays(loanIou.getCreateTime(), i);
        accountFlow.setTransferTime(transDay);
        // 时间判断的
        accountFlow.setBetweenLeft(DateUtils.betweenArr(transDay)[0]);
        // 时间判断的
        accountFlow.setBetweenRight(DateUtils.betweenArr(transDay)[1]);
        accountFlow.setOffsetTime(new Date());
        accountFlow.setDataSource("loan_iou、loan_offset");
        accountFlow.setAmt(lateFee);
        accountFlow.setFeeType(EnumFeeType.REPAID_LATE_FEE.getCode());
        if (null == accountFlowMapper.selectByTransTime(accountFlow)) {
            asyncRepayMentService.asyncInsertAccountFlow(accountFlow);
        }

    }

    /**
     * 冲账到那一项归零
     *
     * @param loanIou
     */
    private void setZeroOffset(LoanIouExtVo loanIou) {
        loanIou.setOffsetLateFee(BigDecimal.ZERO);
        loanIou.setOffsetServiceFee(BigDecimal.ZERO);
        loanIou.setOffsetInterest(BigDecimal.ZERO);
        loanIou.setOffsetPrincipal(BigDecimal.ZERO);
    }

    /**
     * @param orgOffset     当前还款的数据库数据
     * @param beforeloanIou 前一次还款iou快照
     * @param lateFee
     * @return
     */
    private boolean compareRepaidAmt(LoanOffset orgOffset, LoanIou beforeloanIou, BigDecimal lateFee) {
        if (orgOffset.getLateFee().compareTo(lateFee.setScale(4, BigDecimal.ROUND_DOWN)) != 0) {
            return true;
        }
        if (null != beforeloanIou.getRepaidLateFee() && orgOffset.getRepaidLateFee().compareTo(beforeloanIou.getRepaidLateFee()) != 0) {
            return true;
        }
        if (null != beforeloanIou.getRepaidInterest() && orgOffset.getRepaidInterest().compareTo(beforeloanIou.getRepaidInterest()) != 0) {
            return true;
        }
        if (null != beforeloanIou.getRepaidServiceFee() && orgOffset.getRepaidServiceFee().compareTo(beforeloanIou.getRepaidServiceFee()) != 0) {
            return true;
        }
        if (null != beforeloanIou.getRepaidPrincipal() && orgOffset.getRepaidPrincipal().compareTo(beforeloanIou.getRepaidPrincipal()) != 0) {
            return true;
        }
        return false;
    }


    private TreeMap<Integer, List<LoanOffset>> loanOffsetListToTreeMap(List<LoanOffset> loanOffsetList) {
        TreeMap<Integer, List<LoanOffset>> collect = loanOffsetList.stream()
                .collect(
                        Collectors.groupingBy(
                                LoanOffset::getRepayDayIndex,
                                TreeMap::new,
                                Collectors.toList()
                        ));
        return collect;
    }
}
